using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.XPath;
using System.Windows;
using System.Reflection;

namespace EventAbstractionAPI.ApplicationObjectModel
{
    /// <summary>
    /// This class has properties that allows the Application Object Model to be built to suit the unique requirements of users.
    /// </summary>
    internal class SpecifyTree
    {

        #region Singleton Pattern

        static readonly SpecifyTree instance = new SpecifyTree();

        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static SpecifyTree()
        {
        }

        SpecifyTree()
        {
        }

        internal static SpecifyTree Instance
        {
            get
            {
                return instance;
            }
        }

        #endregion

        #region Build Tree
        internal void SpecifyTreeRoot(Application rootNode)
        {
            //specify the application(root) node.
            Globals.Instance.ApplicationNode = new myTreeNodeClass("Application", rootNode, null);
        }

        /// <summary>
        /// Specify the Tiers used in the application. At the root is the Application object. Below this are the tiers that are in this list.
        /// </summary>
        internal void addTier(String name)
        {
            myTreeNodeClass node = new myTreeNodeClass(name, new object(), Globals.Instance.ApplicationNode);
            Globals.Instance.ApplicationNode.Children.AddLast(node);
        }

        /// <summary>
        /// find a tier of TierName and add a child to it.
        /// </summary>
        /// <param name="TierName">the name of the tier to add the child element to.</param>
        /// <param name="Childname">name of child element</param>
        /// <param name="instance">instance of that child</param>
        internal void addTierChild(String TierName, String Childname, Object instance)
        {
            findParentThenAddChild(Globals.Instance.ApplicationNode, TierName, Childname, instance);
        }

        internal void findParentThenAddChild(myTreeNodeClass currentNode, String TierName, String Childname, Object instance)
        {
            if (currentNode.name == TierName)
            {
                currentNode.Children.AddLast(new myTreeNodeClass(Childname, instance, currentNode));
            }
            else
            {
                foreach (myTreeNodeClass node in currentNode.Children)
                {
                    findParentThenAddChild(node, TierName, Childname, instance);
                }
            }
        } 
        #endregion

        #region Query Tree

        internal XPathNodeIterator XPathQuery(String query)
        {
            XPathNavigator nav = Globals.Instance.ApplicationNode.CreateNavigator();
            XPathExpression expr;
            XPathNodeIterator iterator;

            try
            {
                expr = nav.Compile(query);
                iterator = nav.Select(expr);
                return iterator;
            }
            catch (Exception)
            {
                //TODO: handle the error.
                throw;
            }

        }

        /// <summary>
        /// return a myTreeNodeClass object from a query string. That way we can add objects to the tree at runtime.
        /// </summary>
        /// <param name="query">query of node to return.</param>
        /// <returns>A myTreeNodeClass which is an XPathNavigatable tree node of the Application Object Model.</returns>
        private myTreeNodeClass getNode(String query)
        {
            //TODO: should this return a set of nodes???
            throw new Exception("Method not implemented yet");
            //return;
        } 

        #endregion

        internal void addTierChildren(String tierName, Type attributeType)
         {
            //---------------------------------------------------------------------------------------------
            //Search for Classes to add. (this allows the use of static methods of non instanced classes).
            //---------------------------------------------------------------------------------------------

            Assembly assembly = Assembly.GetEntryAssembly();

            Type[] types = assembly.GetTypes();

            foreach (Type type in types)
            {
                Object[] attributes = type.GetCustomAttributes(false);

                foreach (object attribute in attributes)
                {
                    if (attribute.GetType() == attributeType)
                    {
                        findParentThenAddChild(Globals.Instance.ApplicationNode, tierName, type.Name, type);

                        foreach (MethodInfo var in type.GetMethods())
                        {
                            findParentThenAddChild(Globals.Instance.ApplicationNode, type.Name, var.Name, var);
                        }

                        foreach (PropertyInfo var in type.GetProperties())
                        {
                            findParentThenAddChild(Globals.Instance.ApplicationNode, type.Name, var.Name, var);
                        }
                    }
                }
            }

            //---------------------------------------------------------------------------------------------
            //Search for Instances of classes (as public parameters) to add that have annotations above them.
            //---------------------------------------------------------------------------------------------
            
            //TODO: public parameters

            //TODO: also use method returns and properties to obtain instances.
            
        }
    }
}
